package com.kehutong.admin.util;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.coraframework.authz.Subject;
import org.coraframework.authz.SubjectUtil;
import org.coraframework.cache.Cache;
import org.coraframework.cache.support.LRUCache;
import org.coraframework.util.Ioc;
import org.coraframework.util.Objects;

import com.kehutong.admin.entity.Area;
import com.kehutong.admin.entity.Menu;
import com.kehutong.admin.entity.Role;
import com.kehutong.admin.entity.User;
import com.kehutong.admin.util.service.UserService;

public class UserUtils {

	public static final String LOGIN = "LOGIN";
    public static final String CURRENT_USER = "currentUser";
    
    public static final String CACHE_ROLE_LIST = "roleList";
    
    public static final String CACHE_AREA_TREE = "areaTree";
    
    public static final String CACHE_TOP_AREA_TREE = "topAreaTree";
    
    public static final String CACHE_MENU_TREE = "menuTree";

    public static final String CACHE_PERMISSIONS = "permissions";
    
    public static final String LOGIN_FAIL_COUNT = "LOGIN_FAIL_COUNT";
    private static final Integer LOGIN_FAIL_COUNT_LIMIT_TIME = 60 * 10;
    
    /** 登录失败次数限制 */
    private static final Integer maxLoginErrorCount = 3;

    private static UserService userService;
    private static Cache<String, Integer> cache;
    
    static {
    	cache = new LRUCache<>("user-info-cache", 50*1024*1024, LOGIN_FAIL_COUNT_LIMIT_TIME);
    	userService = Ioc.getInstance(UserService.class);
    }

    /**
     * 获取当前登录用户ID
     */
    public static String getUserId() {
        return getSubject().getUserId();
    }
    

    /**
     * 获取当前登录用户信息
     */
    public static User getUser() {
        User user = getSubject().getAttr(CURRENT_USER);
        if (user != null) {
            return user;
        }

        String userId = getUserId();
        if (Objects.nonEmpty(userId)) {
        	user = userService.getUser(userId);
        	getSubject().setAttr(CURRENT_USER, user);
        }
        
        return user;
    }
    
    /** 获取当前登录用户菜单树
     * @throws Exception */
    public static Area getTopAreaTree(boolean forceLoad) throws Exception {
    	final Subject subject = getSubject();
    	
    	Area area = subject.getAttr(CACHE_TOP_AREA_TREE);
        if (area == null || forceLoad) {
        	Map<String, Area> itemList = userService.loadAreas();
        	 if (itemList != null) {
        		 area = TreeEntityUtil.organizeAsTree(itemList, false);
        		 area.getChildList().forEach(item->item.setChildList(Collections.emptyList()));
                 subject.setAttr(CACHE_TOP_AREA_TREE, area);
             }
        }
        return area;
    }
    
    /** 获取当前登录用户菜单树
     * @throws Exception */
    public static Area getAreaTree(boolean forceLoad) throws Exception {
    	final Subject subject = getSubject();
    	
    	Area area = subject.getAttr(CACHE_AREA_TREE);
        if (area == null || forceLoad) {
        	Map<String, Area> itemList = userService.loadAreas();
        	 if (itemList != null) {
        		 area = TreeEntityUtil.organizeAsTree(itemList, false);
                 subject.setAttr(CACHE_AREA_TREE, area);
             }
        }
        return area;
    }
    
    /**
     * <pre>获取当前登录用户角色列表</pre>
     * @throws Exception 
     */
    public static List<Role> getRoleList(User user, boolean flush) {
    	Subject subject = getSubject();
    	List<Role> roleList = getSubject().getAttr(CACHE_ROLE_LIST);
    	
        if (Objects.isNull(roleList) || flush) {
        	roleList = userService.getRoleList(user);
            subject.setAttr(CACHE_ROLE_LIST, roleList);
        }
        
        return roleList;
    }

    /**
     * 获取当前登录用户角色列表
     * @throws Exception 
     */
    public static Collection<Role> getRoleList() throws Exception {
    	final Subject subject = getSubject();
    	
		Collection<Role> roleList = subject.getAttr(CACHE_ROLE_LIST);
        if (roleList == null) {
        	 User user = getUser();
             if (user == null) {
                 return null;
             }
             
             if (user.isAdmin()) {
            	 roleList = userService.loadRoles();
             } else {
            	 roleList = userService.loadRoles(user);
             }
             
             subject.setAttr(CACHE_ROLE_LIST, roleList);
        }
        
        return roleList;
    }


    /** 获取当前登录用户菜单列表
     * @throws Exception */
	public static Map<String, Menu> getMenuList(boolean showAll) throws Exception {
        User user = getUser();
        if (user == null) {
            return null;
        }

        Map<String, Menu> menuList;
        if (user.isAdmin()) {
            menuList = userService.loadMenus(showAll);
        } else {
            menuList = userService.loadMenus(user, showAll);
        }
        
        return menuList;
    }
    
    /** 获取当前登录用户菜单树
     * @throws Exception */
    public static Menu getMenuTree(boolean forceLoad, boolean showAll) throws Exception {
    	final Subject subject = getSubject();
    	
    	Menu menu = subject.getAttr(CACHE_MENU_TREE);
        if (menu == null || forceLoad) {
        	Map<String, Menu> menuList = getMenuList(showAll);
            if (menuList != null) {
            	menu = TreeEntityUtil.organizeAsTree(menuList, true);
                subject.setAttr(CACHE_MENU_TREE, menu);
            }
        }
        
        return menu;
    }

    /**
     * 获取用户菜单权限信息
     *
     * @return
     * @throws Exception 
     */
    public static Set<String> getPermissions(boolean forceLoad) throws Exception {
    	final Subject subject = getSubject();
    	
    	Set<String> permissions = subject.getAttr(CACHE_PERMISSIONS);
        if (permissions == null || forceLoad) {
        	Map<String, Menu> items = getMenuList(true);
        	if (items != null) {
        		permissions = new HashSet<>(items.size());
        		for (Menu menu : items.values()) {
	        		if (Objects.nonEmpty(menu.getPermission())) {
	        			for (String permission : menu.getPermission().split(",")) {
	        				if (Objects.nonEmpty(permission)) {
	        					permissions.add(permission);
	        				}
	        			}
	        		}
	        	}
        		
        	  subject.setAttr(CACHE_PERMISSIONS, permissions);
        	}
        }
    	if (!UserUtils.getUser().isAdmin()) {
    		permissions.remove("sys:menu:edit");
    	}
    	
        return permissions;
    }
    
    /**
     * 获取用户菜单权限信息
     *
     * @return
     * @throws Exception 
     */
    public static Set<String> getPermissions(User user) throws Exception {
        Map<String, Menu> menus;
        if (user.isAdmin()) {
            menus = userService.loadMenus(true);
        } else {
            menus = userService.loadMenus(user, true);
        }
        
    	Set<String> permissions = new HashSet<>(menus.size());
		for (Menu menu : menus.values()) {
    		if (Objects.nonEmpty(menu.getPermission())) {
    			for (String permission : menu.getPermission().split(",")) {
    				if (Objects.nonEmpty(permission)) {
    					permissions.add(permission);
    				}
    			}
    		}
    	}
	
    	if (!user.isAdmin()) {
    		permissions.remove("sys:menu:edit");
    	}
    	
        return permissions;
    }

    /**
     * 更新用户的登录时间以及登录IP
     */
    public static void flushUserLoginTimeAndIp(String loginIp) {
        if (SubjectUtil.getInstance().isValidToken()) {
        	User user = getUser();
    		userService.flushIP(user, loginIp);
        }
    }

    /**
     * 判断是否需要验证验证码
     *
     * @param userName
     * @return
     */
    public static boolean isNeedValidCode(String userName) {
        Integer loginFailNum = cache.get(LOGIN_FAIL_COUNT + "_" + userName);
        if (loginFailNum == null) {
            loginFailNum = 0;
        }

        return loginFailNum >= maxLoginErrorCount;
    }

    /**
     * 登录失败次数增加一次
     *
     * @param userName
     * @return
     */
    public static void increaseLoginErrorCount(String userName) {
        Integer loginFailNum = cache.get(LOGIN_FAIL_COUNT + "_" + userName);
        if (loginFailNum == null) {
            loginFailNum = 0;
        }

        loginFailNum++;
        cache.put(LOGIN_FAIL_COUNT + "_" + userName, loginFailNum);
    }
    
    /**
     * 登录失败次数增加一次
     *
     * @param userName
     * @return
     */
    public static void cleanLoginErrorCount(String userName) {
    	cache.put(LOGIN_FAIL_COUNT + "_" + userName, 0);
    }

    private static Subject getSubject() {
    	return SubjectUtil.getSubject();
    }

}
